package com.ym.medical.service.impl;

import com.ym.common.constant.Constants;
import com.ym.common.enums.SmsTemplateEnum;
import com.ym.common.utils.SmsUtil;
import com.ym.core.redis.RedisKeyEnum;
import com.ym.core.redis.RedisUtil;
import com.ym.core.utils.Assert;
import com.ym.core.utils.tkmybatis.TK;
import com.ym.medical.domain.entity.UserEntity;
import com.ym.medical.enums.SmsCodeTypeEnum;
import com.ym.medical.service.IMessageCodeSV;
import com.ym.medical.service.ISysConfigSV;
import com.ym.medical.service.ISysUserSV;
import com.ym.medical.service.IUserSV;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

/**
 * 短信验证码
 *
 * @author: 李涛
 * @version: 2019年11月18日 9:34
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class MessageCodeSVImpl implements IMessageCodeSV {

	private static final Logger LOGGER = LoggerFactory.getLogger(MessageCodeSVImpl.class);

	@Autowired
	private RedisUtil redisUtil;

	@Autowired
	private IUserSV userSV;

	@Autowired
	private ISysUserSV sysUserSV;

	@Autowired
	private ISysConfigSV sysConfigSV;

	@Override
	public String sentCode(String phone, SmsCodeTypeEnum codeType, SmsTemplateEnum temp) {
		String key = RedisKeyEnum.SEND_PHONE_CODE.build(codeType.getCodeType().name(), phone);
		String keyTimes = RedisKeyEnum.SEND_PHONE_CODE_TIMES.build(codeType.getCodeType().name(), phone);
		int dailySmsTimes = sysConfigSV.getValueByKey(Constants.SysConfigKey.DAILY_SMS_TIMES, Integer::new);
		int smsValidTime = sysConfigSV.getValueByKey(Constants.SysConfigKey.SMS_VALID_TIME, Integer::new);
		int smsIntervalTime = sysConfigSV.getValueByKey(Constants.SysConfigKey.SMS_INTERVAL_TIME, Integer::new);
		// 检测手机号是否存在
		Boolean exist = null;
		switch (codeType.getCodeType()) {
			case SYS:
				exist = sysUserSV.existByPhone(phone);
				break;
			case APP:
				exist = userSV.exist(TK.selectWhere(UserEntity.class).andEqualTo(UserEntity::getPhoneNo, phone).end());
				break;
		}
		if (exist != null) {
			if (codeType.getCheckType() == SmsCodeTypeEnum.CheckType.EXIST_TELL_ME) {
				Assert.isTrue(exist, "手机号码已存在!");
			} else if (codeType.getCheckType() == SmsCodeTypeEnum.CheckType.NOT_EXIST_TELL_ME) {
				Assert.isFalse(exist, "手机号码不存在!");
			}
		}

		// 初始化计数器
		if (!redisUtil.hasKey(keyTimes)) {
			redisUtil.set(keyTimes, "0", 1, TimeUnit.DAYS);
		}

		// 是否超出每天限制
		String limit = redisUtil.get(keyTimes);
		boolean overTimes = (limit != null && Integer.valueOf(limit) > dailySmsTimes);
		Assert.isTrue(overTimes, "当日手机号发送超出限制!");

		// 是否还在发送间隔内
		long expire = redisUtil.getExpire(key);
		if (expire > 0) {
			long invalTime = expire - (TimeUnit.MINUTES.toSeconds(smsValidTime) - smsIntervalTime);
			Assert.isTrue(invalTime > 0, "请在{}秒后再次尝试发送验证码!", invalTime);
		}

		// 发送验证码
		String cacheCode = RandomStringUtils.randomNumeric(4);
		// 验证码有效期设置
		redisUtil.set(key, cacheCode, smsValidTime, TimeUnit.MINUTES);
		// 发送次数累加
		redisUtil.incr(keyTimes, 1);
		SmsUtil.sendSms(phone, temp, cacheCode);

		LOGGER.info("{}获取验证码为:{}", phone, cacheCode);
		return cacheCode;
	}


	@Override
	public boolean validatePhoneCode(String phone, SmsCodeTypeEnum codeType, SmsTemplateEnum temp, String code) {
		String anySmsCode = sysConfigSV.getValueByKey(Constants.SysConfigKey.ANY_SMS_CODE);
		if (anySmsCode.equals(code)) {
			return true;
		}
		String key = RedisKeyEnum.SEND_PHONE_CODE.build(codeType.getCodeType().name(), phone);
		String cacheCode = redisUtil.get(key);
		if (code.equals(cacheCode)) {
			redisUtil.del(key);
			return true;
		}
		return false;
	}
}
